#include "MemoryManager.hpp"
#include <malloc.h>

using namespace My;

namespace My
{
    // 预设定好一些可能会用到的内存块尺寸
    static const uint32_t kBlockSizes[] = 
    {
        // 增量为4
        4,  8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48,
        52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92, 96,

        // 增量为32
        128, 160, 192, 224, 256, 288, 320, 352, 384,
        416, 448, 480, 512, 544, 576, 608, 640, 

        // 增量为64
        704, 768, 832, 896, 960, 1024
    };

    static const uint32_t kPageSize = 8192; // 页大小
    static const uint32_t kAlignment = 4;   // 对齐大小

    // kBlockSizes中的元素数量
    static const uint32_t kNumBlockSizes
        = sizeof(kBlockSizes) / sizeof(kBlockSizes[0]);

    // 最大有效块大小
    static const uint32_t kMaxBlockSize
        = kBlockSizes[kNumBlockSizes - 1]; 

    size_t*        MemoryManager::m_pBlockSizeLookup;
    Allocator*     MemoryManager::m_pAllocators; 
}

 int My::MemoryManager::Initialize()
{
    // one-time initialization    
    // 使用 lambda 表达式和立即执行的函数体来确保 s_bInitialized 的初始化是线程安全的，并且避免了静态初始化顺序问题。
    static bool s_bInitialized = []() 
        {
            // 初始化块内存映射数组
            m_pBlockSizeLookup = new size_t[kMaxBlockSize + 1];

            // 建立映射关系
            // 记录kMaxBlockSize尺寸之下
            //任意内存大小对应kBlockSizes里提供的相匹配的块所在的位置
            size_t j = 0;
            for(size_t i = 0; i <= kMaxBlockSize; i++)
            {                
                if(i > kBlockSizes[j]) ++j;
                m_pBlockSizeLookup[i] = j;
            }

            // 初始化各种尺寸的内存分配器
            m_pAllocators = new Allocator[kNumBlockSizes];
            for (size_t i = 0; i < kNumBlockSizes; i++)
                m_pAllocators[i].Rest(kBlockSizes[i],kPageSize, kAlignment);           

            return true;
        }();
    
     return 0;
}

void My::MemoryManager::Finalize()
{
    delete[] m_pAllocators;
    delete[] m_pBlockSizeLookup;
}

void My::MemoryManager::Tick()
{

}

void* My::MemoryManager::Allocate(size_t size)
{
    Allocator* pAlloc = LookUpAllocator(size);

    if(pAlloc) return pAlloc->Allocate();
    else return malloc(size);
}

void My::MemoryManager::Free(void *p, size_t size)
{
    Allocator* pAlloc = LookUpAllocator(size);
    if (pAlloc) pAlloc->Free(p);
    else free(p);
}

Allocator* My::MemoryManager::LookUpAllocator(size_t size)
{
    if(size < kMaxBlockSize)
        return m_pAllocators + m_pBlockSizeLookup[size];
        else return nullptr;
}